package la.ge.server.gs2ge;

import java.util.Collection;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import la.common.xml.XMLHelper;
import la.common.xml.XPathHelper;
import la.ge.kml.KMLHelper;
import la.gs.GSResultRec;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Thead not safe
 * 
 * @author labile
 * 
 */
public class GSResultRecs2BigNode {

	private Element eNetLinkTemplate;

	private XPathHelper eNetLinkHref;

	private XPathHelper eNetLinkName;

	private Document docTemplate;

	private GSResultRecs2KMLNetLinks resultRecs2KMLNetLinks;

	private XPath xpath;
	
	public static interface GenParam {
		public String genNearResultSetHref(int firstGSResultIndex);
		public String genFarResultSetHref(int firstGSResultIndex);
	}

	public GSResultRecs2BigNode(XMLHelper xmlHelper) {
		try {
			xpath = xmlHelper.createXMLPath(KMLHelper.KML20XMLNSContext);
			docTemplate = xmlHelper
					.sureLoadDocFromClassLoader("/la/ge/server/gs2ge/GSResultRecs2BigNode.xml");
			// get the NetworkLink element from template doc
			eNetLinkTemplate = (Element) xpath.compile(
					"/kml:kml/kml:Document/kml:NetworkLink").evaluate(
					docTemplate, XPathConstants.NODE);

			// remove <NetworkLink> from template doc
			XMLHelper.removeMe( eNetLinkTemplate );

			// build the xpaths for reuse
			eNetLinkHref = new XPathHelper(xpath.compile("kml:Url/kml:href"));
			eNetLinkName = new XPathHelper(xpath.compile("kml:name"));

			resultRecs2KMLNetLinks = new GSResultRecs2KMLNetLinks();
		} catch (XPathExpressionException e) {
			throw new RuntimeException(e);
		}
	}

	public GSResultRecs2BigNode() {
		this(new XMLHelper());
	}

	/**
	 * 
	 * @param gsResultSet
	 * @param firstGSResultIndex
	 *            begin from 0
	 * @param totalGSResultQty
	 * @param genParam 
	 * @return result
	 */
	public Document act(final Collection<GSResultRec> gsResultSet,
			final int firstGSResultIndex, final int totalGSResultQty,
			GenParam genParam) {

		try {
			Document result = (Document) docTemplate.cloneNode(true);
			Collection<Element> eActualNetworkLinks = resultRecs2KMLNetLinks
					.act(gsResultSet, result);

			Element eDocument = (Element) xpath
					.compile("/kml:kml/kml:Document").evaluate(result,
							XPathConstants.NODE);

			// add the actual <NetworkLink>s to this.eDocument
			eDocument.appendChild(result
					.createComment("add the actual <NetworkLink>s"));
			for (Node e : eActualNetworkLinks) {
				e = result.importNode(e, true);
				eDocument.appendChild(e);
			}

			int remainResultQty = totalGSResultQty - firstGSResultIndex
					- gsResultSet.size();
			remainResultQty = remainResultQty < 0 ? 0 : remainResultQty;// non-negative

			// add the near result set loop <NetworkLink>s to this.eDocument
			eDocument
					.appendChild(result
							.createComment("add the near result set <NetworkLink>s to this.eDocument"));
			final int MAX_NEAR_RESULTSET_QTY = 10;
			final int RESULTQTY_IN_RESULTSET = 10;
			int nextNearSetFirstGSResultIndex = firstGSResultIndex + gsResultSet.size();
			for (int nearResultSetQty = 1; remainResultQty > 0
					&& nearResultSetQty <= MAX_NEAR_RESULTSET_QTY; nearResultSetQty++) {
				// near result set <NetworkLink>
				Element eNearResultSetNL = (Element) eDocument
						.appendChild(result.importNode(eNetLinkTemplate, true));

				eDocument.appendChild(eNearResultSetNL);

				eNetLinkHref.sureSetElementTextContent(eNearResultSetNL,
						genParam.genNearResultSetHref( nextNearSetFirstGSResultIndex ) );
				eNetLinkName.sureSetElementTextContent( eNearResultSetNL,
						"Near result:" + nextNearSetFirstGSResultIndex );

				remainResultQty = remainResultQty - RESULTQTY_IN_RESULTSET;
				nextNearSetFirstGSResultIndex += RESULTQTY_IN_RESULTSET;
			}
			
			if (remainResultQty > 0) {// add far result set loop <NetworkLink>
				eDocument.appendChild(result
						.createComment("far result set loop <NetworkLink>"));
				// far result set <NetworkLink>
				Element eFarResultSetNL = (Element) eDocument
						.appendChild(result.importNode(eNetLinkTemplate, true));

				eDocument.appendChild(eFarResultSetNL);

				eNetLinkHref.sureSetElementTextContent(eFarResultSetNL,
						genParam.genFarResultSetHref( nextNearSetFirstGSResultIndex ) );
				eNetLinkName.sureSetElementTextContent(eFarResultSetNL,
						"Far result:" + nextNearSetFirstGSResultIndex);
			}

			return result;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}
